home *** CD-ROM | disk | FTP | other *** search
- /*
- File: PAPPostScriptStuff.c
-
- By Rich Kubota
-
-
- The following are routines used to support the handling of Postscript query
- processing. These are my own routines implemented to provide support for
- the postscript queries from a laserwriter client. All of the postscript
- parsing related functions are included in this file. I'd like to warn the
- reader that this code is written to handle some basic postscript queries as
- sent by Apple LaserWriter Client prior to v8.5.
-
- The design of the routine presented here are to
- 1. recognize postscript queries as opposed to postscript data
- 2. parse postscript commands and return the default response.
-
- This code does not support other Postscript clients or handling of other
- than the default responses. This may not be sufficient to support your
- printer and client.
-
- For more information on processing Postscript queries, contact Adobe for their
- technote on this subject *** Get web address ***
-
- A RIP or printer spooler would have to handle queries in a different manner.
- In order to respond to the queris the spooler code might already have the
- desired response, or may have to delay response until it can query the
- printer to obtain the correct response.
-
- My thanks to Mark Fleming for his help with debugging this code.
-
- Change History
-
- 1. Fixed the prevPtr bug where I return the current packetPtr instead of the previous
- packetPtr in the ProcessPSQuery function
- 0. Moved PostScript parsing code to this file.
-
- */
-
- #include <ctype.h>
- #include "StringUtils.h"
- #include "PAPServerSample.h"
- #include "PAPPostScriptStuff.h"
-
- extern PacketPtr gTempPackPtr;
- char gEOFStr[8] = kEOFStr;
- char gBeginPSStr[8] = "\045\041PS"; // = "%!PS"
- char qBeginQueryStr[8] = kBeginQueryStr;
- char gEndStr[8] = kEndStr;
- char gQueryStr[8] = kQueryStr;
- extern OTLIFO* gFreeQ;
- extern Boolean gDone;
-
-
- // prototypes
-
- Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
- SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll);
-
-
- /*
- The TestDataIsPSQuery is used to determine whether an incoming packet is the initial packet
- for a postscript query, or is a continuing packet of a prelvious query started with some
- previous packet.
- */
- Boolean TestDataIsPSQuery(PacketPtr packetPtr)
- {
- MyEndpointRef *theEp;
- Boolean result;
-
- theEp = packetPtr->theEp;
-
- if (TstInPSQueryFlag(theEp->flags)) // check if the endpoint is already processing a postscript
- result = true; // query for which we have not reached the EndQuery
- else
- {
- result = IsPacketAPSQuery(packetPtr);
- if (result == true)
- {
- #if SHOW_DEBUG_FLOW
-
- DebugStr("\p processing a postscript query;g");
- #endif
- theEp->prevPtr = nil; // initialize the prevPtr field to nil
- SetInPSQueryFlag(theEp->flags);
- theEp->psState = kLookingForEndStr; // indicate that we have identified query beginning
- // and that the next step is to look for the EndQuery string
-
- // set the time Data In timestamp field
- BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataIn, sizeof(OTTimeStamp));
- // reset the numBytesIn field to zero
- theEp->numBytesIn = 0;
-
- }
- }
-
- return result;
- }
-
- /*
- ProcessPSQuery as used in this sample looks for the ps end...query string, then
- gets the response and and sends it
- The routine is designed to be called at deferred task time and uses OTAllocMem
- instead of NewPtr or NewHandle
-
- Note that this routine is responsible for enqueueing the processed packet ptr
- to the freeQ, unless it appears that there is a partial match to a string that
- we are looking for at the end of the buffer. If so, then the packetPtr is
- save with the endpoint ref to be used when the next incoming packet is processed
-
- In this routine, the first while loop is where the contents of the ps query are
- analyzed until the end of the packet is read. As each default response is read,
- an individual OTSnd call is made to send the default response. Each response will
- be made with the T_MORE flag bit set to keep the EOF bit from being sent in the
- PAP responses. This routine does not queue multiple responses to send in a single
- response.
-
- When the end of the packet has been reached, the routine exits the while loop
- and checks for the EOF indicator signaling the end of the query. If the
- EOF is read, then the call is made to SendEmptyPacket, which will result in
- an empty packet with the EOF bit set. The Apple LaserWriter client requires
- receiving a last packet that has the eof bit set to know that the query has been
- completely processed by the printer/server (even if the printer/server has responded
- to all of the query items).
- */
- OSStatus ProcessPSQuery(PacketPtr packetPtr)
- {
- OSStatus err;
- MyEndpointRef *theEp;
- PacketPtr packPtr;
- UInt16 offset;
- UInt16 matchResult;
- Boolean done = false;
- Boolean testflag = false;
-
- theEp = packetPtr->theEp;
- // add the number of bytes in the packet to the numBytesIn field
- theEp->numBytesIn += packetPtr->numBytes;
-
- // set the time Data End timestamp field
- BlockMove((Ptr)&packetPtr->timeStamp, (Ptr)&theEp->timeDataEnd, sizeof(OTTimeStamp));
-
- err = kOTNoError;
- // check whether we need to concatenate the current and previous packet ptrs
- if ((packetPtr == theEp->prevPtr) || (theEp->prevPtr == nil))
- {
- #if SHOW_DEBUG_FLOW
- DebugStr("\p no packet to append");
- #endif
- testflag = false;
- packPtr = packetPtr;
- }
- else
- {
- #if SHOW_DEBUG_FLOW
- DebugStr("\p checking how we append packets");
- #endif
- packPtr = gTempPackPtr;
-
- // copy contents of the previous packet buffer to the new buffer
- BlockMove((Ptr)theEp->prevPtr, (Ptr)packPtr, sizeof(PacketBuffer));
- offset = sizeof(PacketBuffer) - kPAPDataSize + (theEp->prevPtr)->numBytes;
- BlockMove((Ptr)&(packetPtr->data), (Ptr)&(packPtr->data[offset]), packetPtr->numBytes);
- // adjust the numbytes field
- packPtr->numBytes += packetPtr->numBytes;
-
- // enqueue the previous packet ptr back to the gFreeQ since I not expecting a
- // ps default response that overlaps 3 packets.
- // fixed bug here where I release the current packetPtr instead of the previous
- // packetPtr
-
- OTLIFOEnqueue(gFreeQ, &((theEp->prevPtr)->fLink)); // first field is fLink field
- }
-
- while (done == false)
- {
- switch (theEp->psState)
- {
- case kLookingForEndStr:
- matchResult = FindQueryString(packPtr, kLookingForEndStr);
- break;
-
- case kLookingForQueryStr:
- matchResult = FindQueryString(packPtr, kLookingForQueryStr);
- break;
-
- case kLookingForDefaultResponse:
- #if SHOW_DEBUG_FLOW
- if (testflag)
- DebugStr("\p about to call ProcessDefaultResponse");
- #endif
-
- matchResult = ProcessDefaultResponse(packPtr);
- break;
- }
-
- switch (matchResult)
- {
- case kMatch:
- if (theEp->psState == kLookingForDefaultResponse)
- theEp->psState = kLookingForEndStr;
- else
- theEp->psState += 1; // increment the state
- break;
-
- case kPartialMatch:
- done = true; // have to come around with the next packet
- break;
-
- case kNoMatch:
- #if SHOW_DEBUG_FLOW
- DebugStr("\p no match");
- #endif
- // wasn't able to find a match, not even a
- // partial match, so set the pos to the end of the
- // packet
- packPtr->lastPos = packPtr->numBytes;
- done = true; // have to come around with the next packet
- break;
- }
-
- }
-
- // check to see if we see the EOF flag
-
-
- // this is the last packet of this query.
- // check to see if the lastPos field is set to the end of the buffer
- // this tels us the we are not awaiting a pending match for a partial
- // string
- if (packPtr->lastPos == packPtr->numBytes)
- {
- // reset the lastPos field so that we can search for the EOF string as the
- // last few characters
- packPtr->lastPos -= clen(gEOFStr);
- // search for the EOF string at the end of the packet
- matchResult = FindQueryString(packPtr, kLookingForEOFStr);
- // since we reset lastPos, we need to restore it
- packPtr->lastPos = packPtr->numBytes;
-
- if (matchResult == kMatch)
- {
- #if SHOW_DEBUG_FLOW
-
- DebugStr("\p Sending a null packet;g");
- #endif
- // send an empty response with EOF flag set since this is what the Laserwriter
- // client wants to see.
- SendEmptyPacket(packPtr);
- // clear bit that indicates we are processing a postscript query
- ClrInPSQueryFlag(theEp->flags);
- #if SHOW_DEBUG_FLOW
-
- DebugStr("\p Have finished ps query;g");
- #endif
- }
-
- }
-
-
- // check to see whether we processed all of the bytes in the packet.
- if (packPtr->lastPos == packPtr->numBytes)
- {
- theEp->prevPtr = nil; // if so, then don't save this packet in the prevPtr field.
-
- // queue the buffer to the freeQ
- OTLIFOEnqueue(gFreeQ, &(packetPtr->fLink)); // first field is fLink field
- }
- else
- {
- theEp->prevPtr = packetPtr; // we still have bytes to process.
- //adjust the lastPos field
- packetPtr->lastPos = packetPtr->numBytes - (packPtr->numBytes - packPtr->lastPos);
-
- }
-
- return err;
-
- }
-
-
- /*
- DoProcessPSQuery is used as the entry point to processing a postscript query.
- */
- Boolean DoProcessPSQuery(PacketPtr packetPtr)
- {
- OSStatus err = kOTNoError;
- Boolean result = false;
-
- if (TestDataIsPSQuery(packetPtr)) // is the packet a PS query
- {
- result = true; // packet is a PostScript query and is being processed
- err = ProcessPSQuery(packetPtr);
-
- if (err < kOTNoError)
- {
- DoValueBreak(err, "error occured calling ProcessPSQuery #");
- // show the error and quit the main event loop
- gDone = true;
- }
-
- }
- else
- {
- // we aren't processing PS query packets, so exit this routine
- // the IsPacketQuery routine potentially sets the lastPos field
- // so reset it back to 0;
- packetPtr->lastPos = 0;
- }
-
- return result;
- }
-
- /*
- FindString is used to find the iterate through the characters passed in the buffer
- to find the string passed in str. The length of the search string is passed in
- lenStr. pos is a pointer to the index where to start searching. stopChar is the
- last position of the buffer to search
- input
- buffer - pointer to buffer to search
- str - string to match
- lenStr - length of the str to match in buffer
- pos - index where to begin search
- numCharsInBuffer - index of last valid char in buffer
- matchAll - match both upper and lower cases of the input string.
-
- output -
- lenMatched - number of character which were matched. If no match, then
- returns 0.
- pos - index of the next char to look at for the next search.
-
- If the str is not found in the buffer between the pos index and the stopChar,
- *pos returns -1, If a partial match is made at the end of the buffer, return the
- negative offset
- */
-
- Boolean FindString(const char *buffer, char *str, SInt16 lenStr, SInt16 *lenMatched,
- SInt16 *pos, UInt16 numCharsInBuffer, Boolean matchAll)
- {
- SInt16 i;
- SInt16 numToCompare;
- Boolean done = false;
- Boolean ok;
-
- numToCompare = lenStr; // set the number of chars to compare as the string len.
- while (done == false) // search until the flag is set to true.
- {
- // check whether the first character matches the character at the
- // current index position
- if ((buffer[*pos] == str[0]) || ((matchAll == true) && (toupper(buffer[*pos]) ==
- toupper(str[0]))))
- {
- if (numCharsInBuffer < (*pos + lenStr))
- {
- // the has too few remaining characters to compare all of the current
- // string.
- numToCompare = numCharsInBuffer - *pos;
-
- }
- //init OK boolean
- ok = true;
- for (i = 1; (i < numToCompare) && ok; i++)
- {
- if ((buffer[*pos+i] == str[i]) || ((matchAll == true) &&
- (toupper(buffer[*pos+1]) == toupper(str[i]))))
- continue;
- else
- ok = false;
-
- }
- // we've done our testing, now see if we found a match
-
- if (ok == true) // was match found
- {
- *lenMatched = numToCompare; // return the number of chars matched
- *pos += numToCompare; // return the next position in the buffer to start next search
- done = true; // search finished.
- }
- }
-
- if (done == false)
- {
- *pos += 1; // no match found so increment the place to start the next search iteration
- if ((*pos+1) == numCharsInBuffer)
- {
- // no more characters in the buffer to search
- *lenMatched = 0; // set len matched to nothing
- done = true; // search finished.
- }
- }
-
- }
-
- return (*lenMatched != 0);
- }
-
- /*
- IsPacketAPSQuery is used to check whether the contents of a packet are the beginning of
- a PostScript query. This can be found since there will be the signature stuff
- at the beginning of the query.
- */
- Boolean IsPacketAPSQuery(PacketPtr packetPtr)
- {
- SInt16 lenMatched, pos;
- Boolean result;
-
- pos = 0; // start search from the beginning of the buffer
- result = FindString((char*)&(packetPtr->data), gBeginPSStr, clen(gBeginPSStr),
- &lenMatched, &pos, 4, kCaseMatchAll); // must find the "%!PS" string in position 3 & 4 of the buffer
- if (result == true)
- {
- // the search found something in the first 4 characters of the buffer
- if (lenMatched < clen(gBeginPSStr))
- {
- result = false; // didn't match 2 characters
- }
- else
- {
- result = FindString((char*)&(packetPtr->data), qBeginQueryStr,
- clen(qBeginQueryStr), &lenMatched,
- &pos, 24, kCaseMatchAll); // must find "Query\015" in pos 15 - 20 of the buffer
- if (result == true)
- {
- if (lenMatched < clen(qBeginQueryStr))
- result = false;
- else
- {
- // we're going to return a true result, to set the packetPtr.lastPos
- // field to the current pos field. When we start our next search, we
- // do so at this position.
- packetPtr->lastPos = pos;
- }
- }
- }
- }
-
- return result;
- }
-
- UInt16 FindQueryString(PacketPtr packetPtr, SInt16 whichStr)
- {
- SInt16 lenMatched, pos;
- UInt16 result1, len;
- Boolean result;
- char *str;
-
- result1 = kNoMatch; // initialize to no Match
- pos = packetPtr->lastPos; // start search from where we last stopped looking in buffer
- // first find the query prefix
-
- switch (whichStr)
- {
- case kLookingForEndStr:
- str = gEndStr;
- len = clen(gEndStr);
- break;
-
- case kLookingForQueryStr:
- str = gQueryStr;
- len = clen(gQueryStr);
- break;
-
- case kLookingForEOFStr:
- str = gEOFStr;
- len = clen(gEOFStr);
- break;
- }
-
- result = FindString((char*)&(packetPtr->data), str, len,
- &lenMatched, &pos, packetPtr->numBytes, kCaseMustMatch);
-
- if (result == true)
- {
- // check that the proper string len was returned
- if (lenMatched < len)
- {
- // we have reached the end of the buffer and have found a partial match.
- result1 = kPartialMatch;
- // set the lastPos field to where the partial match begins
- packetPtr->lastPos = pos - lenMatched;
- }
- else
- {
- // all parts found
- result1 = kMatch;
- // complete match found, so advance the lastPos field
- packetPtr->lastPos = pos;
- }
- }
-
- return result1;
-
- }
-
-
- UInt16 ProcessDefaultResponse(PacketPtr packetPtr)
- {
- OTResult otErr;
- UInt32 pos, begin;
- UInt16 result;
- char response[255];
-
- // since we've found the end of the query chars, then there is at least a
- // partial match
- result = kPartialMatch;
- // set pos to the lastPos position since this is the beginning of
- // the query
- begin = pos = packetPtr->lastPos;
-
- // remove the leading space character which may precede the default response
- while (packetPtr->data[pos] == kSpaceChar)
- {
- pos++;
- begin++;
- }
-
- // gather the default response characters
- while ((packetPtr->data[pos] != kReturnChar) && (pos < packetPtr->numBytes))
- {
- // gather default response characters
- response[pos - begin] = packetPtr->data[pos];
- // increment the counter
- pos++;
- }
-
- // go ahead and set the terminating null character for the "c" string
- if ((pos - begin) < 255)
- response[pos - begin] = 0;
- else
- {
- // this should never happen, but if it does, I want to know about it.
- #if SHOW_DEBUG_FLOW
- // DebugStr((const unsigned char *)"\p response greater than 255 chars");
- #endif
- response[255] = 0;
- }
-
- if (packetPtr->data[pos] == kReturnChar)
- {
- // we have found the end of the default response
- // catenate a line feed character to the response
-
- ccatchr(response, kLineFeedChar, 1);
-
- otErr = OTSnd((packetPtr->theEp)->ep, response, clen(response), T_MORE);
- if (otErr < 0)
- {
- #if SHOW_DEBUG_FLOW
- DoValueBreak((long)otErr, "error occured sending PS response #");
- #endif
- }
- packetPtr->lastPos = pos;
- result = kMatch;
- }
-
- #if SHOW_DEBUG_FLOW
- ccatchr(response, ';', 1);
- ccatchr(response, 'g', 1);
- c2p(response);
- // DebugStr((const unsigned char*)response);
- #endif
-
-
- return result;
- }
-
- /*
- SendEmptyPacket is implemented to send an empty packet with the T_MORE flag not set.
- The Apple LaserWriter client requires that the last response to a Postscript query
- have the EOF flag set. By having the EOM option enabled and sending a packet with
- the T_MORE flag not set, OT/PAP will send an empty packet that has the EOF bit set.
- */
- void SendEmptyPacket(PacketPtr packetPtr)
- {
- OTResult err;
- char response[16];
-
- err = OTSnd((packetPtr->theEp)->ep, &response, 0, 0);
- if (err != kOTNoError)
- {
- #if SHOW_DEBUG_FLOW
- DoValueBreak((long)err, "error occured sending empty packet #");
- #endif
- }
-
- }